/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.cef.command;

import cef.command.Command;
import cef.command.CommandException;
import coalescent.cef.event.RunRecursionEvent;
import coalescent.data.meta.MetaInf_AC;
import coalescent.data.meta.MetaInf_Mngr;
import coalescent.model.PopGenModel;
import coalescent.recursion.Recursion;
import coalescent.recursion.RecursionListener;
import coalescent.recursion.listener.exe.ACinCacheCounter;
import coalescent.recursion.listener.exe.ExactRecursionExeEvent;
import coalescent.statistic.AC;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Runs post-order recursion implementation on a sample configuration with arbitrary number of
 * computations based on it.
 *
 * @author Susanta Tewari
 * @version 1.0 Dec 3, 2010
 * @param <L>
 * @param <E>
 */
public class RunRecursionCommand<L extends RecursionListener, E extends RunRecursionEvent<L>>
        extends Command<E> {

    /** recursion exe computation: recursion cache size counter */
    private final RecursionCacheACCounter cache_ac_counter;
    private final List<Integer> initCacheSize;    // init cache size

    /** recursion computations */
    private final List<RecursionListener> comps;

    public RunRecursionCommand(final E event) {

        super(event);

        int eventsToMRCA = event.getStatistic().eventsToMRCA();

        initCacheSize    = Collections.nCopies(eventsToMRCA, 0);
        cache_ac_counter = new RecursionCacheACCounter();
        comps            = new ArrayList<>(10);
    }

    @Override
    public void executeImpl() throws CommandException, InterruptedException {

        final E event = getEvent();


        // set model
        final PopGenModel model = event.getModel();
        final AC ac             = event.getStatistic();

        ac.setModel(model);


        // recursion
        final Recursion recursion = createRecursion(ac);

        recursion.addExactRecursionExeListener(cache_ac_counter);

        for (final RecursionListener comp : event.getListeners()) {

            comps.add(comp);
            recursion.addExactRecursionEventListener(comp);
        }

        recursion.runRecursion();


        // meta-inf
        final MetaInf_Mngr metaInf_mngr = event.getMetaInf();

        if (metaInf_mngr.isActive()) {

            MetaInf_AC metaInf_ac = metaInf_mngr.hasInfo()
                                    ? metaInf_mngr.load() : metaInf_mngr.getDefault();

            for (RecursionListener comp : comps) {
                metaInf_ac.processComputation(comp);
            }

            metaInf_mngr.store(metaInf_ac);
        }
    }

    public static boolean isCancellable() {
        return true;
    }

    public boolean add(RecursionListener recursionListener) {
        return comps.add(recursionListener);
    }

    /**
     * Returns the exact recursion outputListeners that were attached to this command. These
     * outputListeners compute various aspects and their {@code toString} method provides a textual
     * summary of the results.
     *
     * @return recursion computations
     */
    public List<RecursionListener> getRecursionListeners() {

        checkCmdFinished();

        return comps;
    }

    /**
     * @return
     */
    public List<Integer> getCurrentRecursionCacheSize() {

        if (!cache_ac_counter.isReady()) return initCacheSize;

        return cache_ac_counter.getCacheSize();
    }

    /**
     * @param ac
     * @return
     */
    protected Recursion createRecursion(final AC<?, ?> ac) {
        return new Recursion(ac);
    }

    /**
     * @version version
     * @since 1.4.0
     * @history Created on 03/31/2013
     * @author Susanta Tewari
     */
    public static class DEFAULT
            extends RunRecursionCommand<RecursionListener, RunRecursionEvent.DEFAULT> {

        public DEFAULT(final RunRecursionEvent.DEFAULT event) {
            super(event);
        }
    }

    /**
     * Class description
     * @version        Enter version here..., 12/10/29
     * @author         Susanta Tewari
     */
    private class RecursionCacheACCounter extends ACinCacheCounter {

        @Override
        public void receivedExactRecursionExeEvent(final ExactRecursionExeEvent event) {

            super.receivedExactRecursionExeEvent(event);


            // prepare the update string
            final long totalCacheSize = event.getTotalCacheSize();
            final String updateString = "Total Cache Size: " + totalCacheSize;

            setUpdate(updateString);
        }
    }
}
